<?php


\vendor\laravel\framework\src\Illuminate\Routing\Router.php
实例化时机。
经过测试，是在 \public\index.php 的
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
时实例化的。真正实例化的是 \app\Http\Kernel.php，继承了
\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
前者没有构造方法，后者的构造方法是
    public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;

        $router->middlewarePriority = $this->middlewarePriority;

        foreach ($this->middlewareGroups as $key => $middleware) {
            $router->middlewareGroup($key, $middleware);
        }

        foreach ($this->routeMiddleware as $key => $middleware) {
            $router->middleware($key, $middleware);
        }
    }
可以看到也是依赖注入的，实例化该类时就会实例化 Router。



// =========================================================

\routes\api.php
\routes\console.php
\routes\web.php


路由加载

\app\Providers\RouteServiceProvider.php
    public function boot()
    {
        //

        parent::boot();
    }
    public function map()
    {
        $this->mapApiRoutes();

        $this->mapWebRoutes();

        //
    }
    protected function mapWebRoutes()
    {
        Route::group([
            'middleware' => 'web',
            'namespace' => $this->namespace,
        ], function ($router) {
            require base_path('routes/web.php');
        });
    }
    protected function mapApiRoutes()
    {
        Route::group([
            'middleware' => 'api',
            'namespace' => $this->namespace,
            'prefix' => 'api',
        ], function ($router) {
            require base_path('routes/api.php');
        });
    }

上面这个类继承了 
\vendor\laravel\framework\src\Illuminate\Foundation\Support\Providers\RouteServiceProvider.php
    public function boot()
    {
        $this->setRootControllerNamespace();

        if ($this->app->routesAreCached()) {
            $this->loadCachedRoutes();
        } else {
            $this->loadRoutes();

            $this->app->booted(function () {
                $this->app['router']->getRoutes()->refreshNameLookups();
            });
        }
    }
    protected function loadRoutes()
    {
        $this->app->call([$this, 'map']);
    }
所以子类的 map 方法就自动执行了。


另外可以看到
routes/web.php 对应了 web 中间件，
routes/api.php 对应了 api 中间件。
但二者有不同：就是 prefix。
web 的没有 prefix，那就是直接访问 http://x.y.z:port/路由；
api 的有 prefix，那就需要在访问时添加前缀 http://x.y.z:port/prefix值/路由；
其中，路由指的是 routes/web.php 和 routes/api.php 各自文件中定义的，
比如 web.php 中的
    Route::get('/', function () {
        return view('welcome');
    });
    访问时就是 http://x.y.z:port/；
api.php 中的
    Route::get('/user', function (Request $request) {
        return $request->user();
    })->middleware('auth:api');
    访问时就是 http://x.y.z:port/api/user；

由此，也知，各自文件中定义的路由，其实是相对的，相对于 prefix 配置项，也就是子路由。

另外说明下，routes\api.php 和 routes\web.php 内直接使用 Route::get/post/put 等方法，
就是使用了 Facade 的功能。也就是从容器中实例化 Route，然后调用 get/post/put 等方法。
具体是实例化什么，从 config\app.php 中的 aliases 数组可以看到 
'Route' => Illuminate\Support\Facades\Route::class,
那么找文件 Illuminate\Support\Facades\Route.php：
发现该类中只有一个方法：
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
那么肯定有绑定 key 为 router 的依赖。

而 \vendor\laravel\framework\src\Illuminate\Routing\RoutingServiceProvider.php
确实存在一个方法：
    protected function registerRouter()
    {
        $this->app['router'] = $this->app->share(function ($app) {
            return new Router($app['events'], $app);
        });
    }



// =========================================================

相关类
\vendor\laravel\framework\src\Illuminate\Routing\Route.php
\vendor\laravel\framework\src\Illuminate\Routing\Router.php
\vendor\laravel\framework\src\Illuminate\Routing\RouteCollection.php

关系是：
Route::get/post/put()
实例化 Router；

    public function get($uri, $action = null)
    {
        return $this->addRoute(['GET', 'HEAD'], $uri, $action);
    }
    public function post($uri, $action = null)
    {
        return $this->addRoute('POST', $uri, $action);
    }
    public function put($uri, $action = null)
    {
        return $this->addRoute('PUT', $uri, $action);
    }
    public function patch($uri, $action = null)
    {
        return $this->addRoute('PATCH', $uri, $action);
    }
    public function delete($uri, $action = null)
    {
        return $this->addRoute('DELETE', $uri, $action);
    }
    public function options($uri, $action = null)
    {
        return $this->addRoute('OPTIONS', $uri, $action);
    }
    public function any($uri, $action = null)
    {
        $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];

        return $this->addRoute($verbs, $uri, $action);
    }
    public function match($methods, $uri, $action = null)
    {
        return $this->addRoute(array_map('strtoupper', (array) $methods), $uri, $action);
    }


    protected function addRoute($methods, $uri, $action)
    {
        return $this->routes->add($this->createRoute($methods, $uri, $action));
    }

    protected function createRoute($methods, $uri, $action)
    {
        // If the route is routing to a controller we will parse the route action into
        // an acceptable array format before registering it and creating this route
        // instance itself. We need to build the Closure that will call this out.
        if ($this->actionReferencesController($action)) {
            $action = $this->convertToControllerAction($action);
        }

        // 确实这里执行了很多次；
        $route = $this->newRoute(
            $methods, $this->prefix($uri), $action
        );

        // If we have groups that need to be merged, we will merge them now after this
        // route has already been created and is ready to go. After we're done with
        // the merge we will be ready to return the route back out to the caller.
        if ($this->hasGroupStack()) {
            $this->mergeGroupAttributesIntoRoute($route);
        }

        $this->addWhereClausesToRoute($route);

        return $route;
    }

\vendor\laravel\framework\src\Illuminate\Routing\Route.php
    // 这里的构造方法执行了多次；
    public function __construct($methods, $uri, $action)
    {
        var_dump('\vendor\laravel\framework\src\Illuminate\Routing\Route.php __construct');echo "<br/>";
        $this->uri = $uri;
        $this->methods = (array) $methods;
        $this->action = $this->parseAction($action);

        if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) {
            $this->methods[] = 'HEAD';
        }

        if (isset($this->action['prefix'])) {
            $this->prefix($this->action['prefix']);
        }
    }

总结就是：每次调用 Route::get() 等方法，都会 new 一个 Route。
一个 Route 对应一个路由。
Router 则管理这些路由。
每次new 的路由都被 $this->routes->add() 了。
那么 $this->routes 是什么？
在 \vendor\laravel\framework\src\Illuminate\Routing\Router.php 的构造方法中看到：
    public function __construct(Dispatcher $events, Container $container = null)
    {
        $this->events = $events;
        $this->routes = new RouteCollection;
        $this->container = $container ?: new Container;

        $this->bind('_missing', function ($v) {
            return explode('/', $v);
        });
    }

\vendor\laravel\framework\src\Illuminate\Routing\RouteCollection.php 有如下两个方法：
    public function add(Route $route)
    {
        $this->addToCollections($route);

        $this->addLookups($route);

        return $route;
    }

    protected function addToCollections($route)
    {
        $domainAndUri = $route->domain().$route->getUri();

        foreach ($route->methods() as $method) {
            $this->routes[$method][$domainAndUri] = $route;
        }

        $this->allRoutes[$method.$domainAndUri] = $route;
    }
也就是说，所有的路由都在 $this->allRoutes 数组中。

// =========================================================


和响应的关系
每个路由单独进行响应。
具体见response 文档。


// =========================================================

测试route响应的具体过程
关键就是在哪里对路由进行匹配的；
也就是说，对于一个请求，框架一开始根据 \routes\web.php 和 \routes\api.php 的配置注册了很多的路由；
之后就是在这些路由中，匹配当前请求的路由，进行处理。
我一开始认为，既然是对路由的处理，应该还是在 Router 和 Route 两个类中。
果然发现 \vendor\laravel\framework\src\Illuminate\Routing\Router.php
    这个方法的文档注释也说明了功能。
    /**
     * Dispatch the request to a route and return the response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function dispatchToRoute(Request $request)
    {
        通过这里的注释，可以看到，就是找一个匹配当前请求的路由。
        // First we will find a route that matches this request. We will also set the
        // route resolver on the request so middlewares assigned to the route will
        // receive access to this route instance for checking of the parameters.
        $route = $this->findRoute($request);

        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        $this->events->fire(new Events\RouteMatched($route, $request));

        $response = $this->runRouteWithinStack($route, $request);

        return $this->prepareResponse($request, $response);
    }

另外，具体响应的入口是在
\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();
        // 上面是框架的初始化；
        // 下面就是响应当前的路由。

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
看到了 dispatchToRouter();
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }

Route::get('/', 'Web\TestController@test');
\vendor\laravel\framework\src\Illuminate\Routing\Router.php
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }
    public function dispatchToRoute(Request $request)
    {
        // First we will find a route that matches this request. We will also set the
        // route resolver on the request so middlewares assigned to the route will
        // receive access to this route instance for checking of the parameters.
        $route = $this->findRoute($request);

        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        $this->events->fire(new Events\RouteMatched($route, $request));

        $response = $this->runRouteWithinStack($route, $request);

        return $this->prepareResponse($request, $response);
    }
    protected function findRoute($request)
    {
        // object(Illuminate\Routing\RouteCollection)#24
        // echo '<br/><hr/>Route findRoute() $this->routes<br/>';var_dump($this->routes);echo '<hr/><br/>';
        $this->current = $route = $this->routes->match($request);// 返回的还是 Route 对象本身；

        $this->container->instance('Illuminate\Routing\Route', $route);

        return $route;
    }

\vendor\laravel\framework\src\Illuminate\Routing\RouteCollection.php
    public function match(Request $request)
    {
        // string(3) "GET"
        // echo '<br/><hr/>RouteCollection match() $request->getMethod()<br/>';var_dump($request->getMethod());echo '<hr/><br/>';
        $routes = $this->get($request->getMethod());
        /*
        kakatest2('RouteCollection match() $routes', $routes);
        array(1) {
          ["/"]=>
          object(Illuminate\Routing\Route)#84 (11) {
            ["uri":protected]=>
            string(1) "/"
            ["methods":protected]=>
            array(2) {
              [0]=>
              string(3) "GET"
              [1]=>
              string(4) "HEAD"
            }
            ["action":protected]=>
            array(6) {
              ["middleware"]=>
              string(3) "web"
              ["uses"]=>
              string(44) "App\Http\Controllers\Web\TestController@test"
              ["controller"]=>
              string(44) "App\Http\Controllers\Web\TestController@test"
              ["namespace"]=>
              string(20) "App\Http\Controllers"
              ["prefix"]=>
              NULL
              ["where"]=>
              array(0) {
              }
            } 
            ...
        */

        // First, we will see if we can find a matching route for this current request
        // method. If we can, great, we can just return it so that it can be called
        // by the consumer. Otherwise we will check for routes with another verb.
        $route = $this->check($routes, $request);
        // 在多个路由中 $routes 找到和当前 $request 相匹配（每个 $route->matches()）的那个 route；
        // 这里没有传递第三个参数，那么就是默认值true，也就是请求方法也要严格一致。

        // 找到了匹配的，执行 if；
        if (! is_null($route)) {
            return $route->bind($request);
        }
        // 没有找到的话，继续往下走；

        // 比如，一个路由，支持 POST，但当前是 GET 请求；

        // If no route was found we will now check if a matching route is specified by
        // another HTTP verb. If it is we will need to throw a MethodNotAllowed and
        // inform the user agent of which HTTP verb it should use for this route.
        $others = $this->checkForAlternateVerbs($request);

        // 找到这些请求方式，比如 ['POST', 'OPTIONS']；
        // 如果有，则执行 if；
        if (count($others) > 0) {
            // 只允许 OPTIONS 请求方式；
            return $this->getRouteForMethods($request, $others);
        }

        throw new NotFoundHttpException;
    }
    protected function check(array $routes, $request, $includingMethod = true)
    {
        // $routes 就是根据当前请求方法找到的 路由和 \vendor\laravel\framework\src\Illuminate\Routing\Route.php 的键值对；
        // $value 就是每个 Illuminate\Routing\Route 实例；
        return Arr::first($routes, function ($value) use ($request, $includingMethod) {
            return $value->matches($request, $includingMethod);
        });
    }

\vendor\laravel\framework\src\Illuminate\Routing\Route.php
    public function matches(Request $request, $includingMethod = true)
    {
        kakatest3('Route matches() $this->uri', $this->uri);
        $this->compileRoute();

        foreach ($this->getValidators() as $validator) {
            if (! $includingMethod && $validator instanceof MethodValidator) {
                continue;
            }

            if (! $validator->matches($this, $request)) {
                return false;
            }
        }

        return true;
    }
    protected function compileRoute()
    {
        $optionals = $this->extractOptionalParameters();
        kakatest3('Route compileRoute() $this->uri', $this->uri);
        $uri = preg_replace('/\{(\w+?)\?\}/', '{$1}', $this->uri);
        // kakatest2('$uri', $uri);// string(21) "{year}/{month}/{date}"

        // kakatest3('Route compileRoute() $this->wheres', $this->wheres);

        $this->compiled = (
            new SymfonyRoute($uri, $optionals, $this->wheres, [], $this->domain() ?: '')
        )->compile();

        kakatest2('Route compileRoute() $this->compiled', $this->compiled);
        /*
            Route::get('{year?}111/222{month?}/{date?}', 'Web\TestController@test')->where('date','[0-9]+')->where(['year'=>'[0-9]+', 'month'=>'[0-9]+']);; 的路由时：
            object(Symfony\Component\Routing\CompiledRoute)#93 (8) {
              ["variables":"Symfony\Component\Routing\CompiledRoute":private]=>
              array(3) {
                [0]=>
                string(4) "year"
                [1]=>
                string(5) "month"
                [2]=>
                string(4) "date"
              }
              ["tokens":"Symfony\Component\Routing\CompiledRoute":private]=>
              array(4) {
                [0]=>
                array(4) {
                  [0]=>
                  string(8) "variable"
                  [1]=>
                  string(1) "/"
                  [2]=>
                  string(6) "[0-9]+"
                  [3]=>
                  string(4) "date"
                }
                [1]=>
                array(4) {
                  [0]=>
                  string(8) "variable"
                  [1]=>
                  string(0) ""
                  [2]=>
                  string(6) "[0-9]+"
                  [3]=>
                  string(5) "month"
                }
                [2]=>
                array(2) {
                  [0]=>
                  string(4) "text"
                  [1]=>
                  string(7) "111/222"
                }
                [3]=>
                array(4) {
                  [0]=>
                  string(8) "variable"
                  [1]=>
                  string(1) "/"
                  [2]=>
                  string(6) "[0-9]+"
                  [3]=>
                  string(4) "year"
                }
              }
              ["staticPrefix":"Symfony\Component\Routing\CompiledRoute":private]=>
              string(0) ""
              ["regex":"Symfony\Component\Routing\CompiledRoute":private]=>
              string(73) "#^/(?P[0-9]+)111/222(?:(?P[0-9]+)(?:/(?P[0-9]+))?)?$#s"
              ["pathVariables":"Symfony\Component\Routing\CompiledRoute":private]=>
              array(3) {
                [0]=>
                string(4) "year"
                [1]=>
                string(5) "month"
                [2]=>
                string(4) "date"
              }
              ["hostVariables":"Symfony\Component\Routing\CompiledRoute":private]=>
              array(0) {
              }
              ["hostRegex":"Symfony\Component\Routing\CompiledRoute":private]=>
              NULL
              ["hostTokens":"Symfony\Component\Routing\CompiledRoute":private]=>
              array(0) {
              }
            }
        */
    }
    public static function getValidators()
    {
        if (isset(static::$validators)) {
            return static::$validators;
        }

        // To match the route, we will use a chain of responsibility pattern with the
        // validator implementations. We will spin through each one making sure it
        // passes and then we will know if the route as a whole matches request.
        return static::$validators = [
            new UriValidator, new MethodValidator,
            new SchemeValidator, new HostValidator,
        ];
        正是
        \vendor\laravel\framework\src\Illuminate\Routing\Matching\UriValidator.php
            public function matches(Route $route, Request $request)
            {
                $path = $request->path() == '/' ? '/' : '/'.$request->path();

                return preg_match($route->getCompiled()->getRegex(), rawurldecode($path));
            }
        \vendor\laravel\framework\src\Illuminate\Routing\Matching\MethodValidator.php
            public function matches(Route $route, Request $request)
            {
                return in_array($request->getMethod(), $route->methods());
            }
        \vendor\laravel\framework\src\Illuminate\Routing\Matching\SchemeValidator.php
            public function matches(Route $route, Request $request)
            {
                if ($route->httpOnly()) {
                    return ! $request->secure();
                } elseif ($route->secure()) {
                    return $request->secure();
                }

                return true;
            }
        \vendor\laravel\framework\src\Illuminate\Routing\Matching\HostValidator.php
            public function matches(Route $route, Request $request)
            {
                if (is_null($route->getCompiled()->getHostRegex())) {
                    return true;
                }

                return preg_match($route->getCompiled()->getHostRegex(), $request->getHost());
            }
    }



\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php
    public function then(Closure $destination)
    {
        $firstSlice = $this->getInitialSlice($destination);

        $callable = array_reduce(
            array_reverse($this->pipes), $this->getSlice(), $firstSlice
        );

        return $callable($this->passable);
    }

















// Route::get('/', function () {
//     echo 132;
//     // return view('welcome');
// });



